﻿package code {
	import flash.geom.Rectangle;
	import flash.events.*;	
	// creates the ball
	public class Ball extends GameObject {
		private const GRAVITY:Number = 1;
		private const MAX_FALL:Number = 50;
		private const BOUNCE:Number = -.8;
		private var mass:int;
		public var reset, launched, gravityIsOn, hasLaunched, reverse:Boolean;	  // reset, launched, gravity on, launched
		
		//HUD Update variables
		public var score:Number = 0;
		public var ballsLeft:Number = 3;
		
		//Collision variables
		private var xBound:Number = 665;
		private var radius:Number = 30;	
		
		// center of circle
		private var center:Vertex;
		
		private var velocity:Vertex;
		
		private var dt:Number;
		
		private var collisionPoint:Vertex;
		private var ballVertices:Array;
		
		public function Ball(shapeType:String, radius:Number, sides:Number, color:uint, thickness:Number, hollow:Boolean) {
			super(shapeType, radius, sides, color, thickness, hollow);
			mass = 5;
			launched = true;
			hasLaunched = false;
			gravityIsOn = false;		
			reset = true; //Is the ball being reset?
			ballVertices = new Array();
			addEventListener(MouseEvent.MOUSE_DOWN, dragBall);
			addEventListener(MouseEvent.MOUSE_UP, undragBall);
		}
		
		public function dragBall(e:Event){
			startDrag();
		}
		
		public function undragBall(e:Event){
			stopDrag();
			gravityIsOn = true;
		}
		
		// launched the ball by changing variables
		public function launch(){
			vx = 0 ;
			launched = false;
			hasLaunched = true;
			reset = true;
		
		}
		
		public function update(collisionObjects:Array, spring:Spring, currentTime:Number, quadTree:QuadTree){
			currentTime = currentTime / 100;
			
			//Normal checks if not being reset
			if(reset == true)
			{
				checkCollision(quadTree);
				moveBall(currentTime);
				
				if(!launched)
					checkSpring(spring);		
				

			}
			
			//Reset the ball and the spring for a new launch
			if(reset == false)
			{
				//Initial spring position
				spring.x = 725;
				spring.y = 720;
				
				//Initial ball position
				x = 730;
				y = 700;
				
				launched = true; //Prepare for launch				
			}
		}
		
		// moves the ball using euler and time
		public function moveBall(currentTime:Number){
			
			if(gravityIsOn && vy < MAX_FALL){
				vy += GRAVITY;
			}
			
			x += vx * currentTime;  // changes velocity over time
			y += vy * currentTime;
		}
		
		public function checkCollision(quadTree:QuadTree){
			var quadIn = quadTree.getContainingQuad(this);
			var quadTreeArray = new Array();
			quadTreeArray = quadTree.collide(quadIn, quadTreeArray);
			if(quadTreeArray != null){
				for each(var quadToCheck in quadTreeArray){
					for each(var l:Line in quadToCheck.collisionObjects){
						dt = -1;
						if(l.a != null && l.b != null){
							// sets up variables by calculating them
							getCenter();  // calculates current center of ball
							getVelocity(); // calcualtes current velocity vector of ball
							l.getNormal(); // calculates normal vector of ball
							l.getPoint(); // calculates pointD on line 
							var onLine:Number = closestPointSegment(l);
							if(onLine < 1 && onLine > 0)
							{
								if(checkLine(l) && dt < 1 && dt >= 0){
									affectBall(l, l.go);
									//If ball has reached the bottom of the table, reset.
									if(y > stage.stageHeight - radius && x < xBound)
									{
										ballsLeft --; //Lose one "life"
										reset = false; //Reset the ball
										hasLaunched = false;
										break;
									}							
									
									//Change the color of the object once hit
									if(l.go.shapeType == "Polygon") //only change polygons
									{
										score += 10; //get points for each hit
										l.go.shape.color = Math.random() * 0xFFFFFF;; //Each collision produces random colors for objects
										l.go.shape.renderVertices(); //Redraw with updated color
										l.go.deform(1.25);									
									}
									
									break;
								}
							}
						}
					}
				}
			}
		}

		// springs to a certain point
		public function checkSpring(spring:Spring){
			spring.springTo(this, new Vertex(725, 650));
			launched = true;
			gravityIsOn = true;
		}	
		
		public function getCenter(){
			ballVertices = new Array();
			center = new Vertex(x , y );
			ballVertices.push(new Vertex(x - shape.radius, y), new Vertex(x, y - shape.radius), new Vertex(x + shape.radius, y), new Vertex(x, y + shape.radius));
		}
		
		public function getVelocity(){
			velocity = new Vertex(vx, vy);
		}
		
		public function closestPointSegment(line:Line):Number{
			var ab:Vertex = Vertex.subtract(line.b, line.a);
			var ifHits:Number;
			for each(var circleVert:Vertex in ballVertices){  // checks for entire ball, not just center
				ifHits = Vertex.dot(Vertex.subtract(center, line.a), ab) / Vertex.dot(ab, ab);
				if(ifHits < 0){
					ifHits = 0;
				}
				if(ifHits > 1){
					ifHits = 1;
				}
				if(ifHits > 0 && ifHits < 1)
					break;  // if there is a collision we go go
			}
			
			return ifHits;
		}
		
		// from Real Time Collision Detection
		// int IntersectMovingSpherePlane(Sphere s, Vector v, Plane p, float &t, Point &q)
		// Sphere = ball, Vector = velocity vector, Plane = p, t = dt (change in time), point = origin + velocity vector
		public function checkLine(line:Line):Boolean{

			var dist:Number = Vertex.dot(line.normal, center) - line.pointD;  //  float dist = Dot(p.n, s.c) - p.d;
			if(Math.abs(dist) <= shape.radius){
				dt = 0;
				collisionPoint = center;

				return true;
			}
			else{
				var denom:Number = Vertex.dot(line.normal, velocity); // gets scalar of the normal vector of line and velocity vector to see where they hit
				if(denom * dist >= 0){  // if the ball is moving away they do not hit
					return false;
					
				}
				else{  // it must be moving towards plane
					var centerToPlane:Number = dist > 0 ? shape.radius : shape.radius;  // it does not like it when the radius is negative, probably because that would mean its colliding on the other side of the line, which is our case is impossible
					dt = (centerToPlane - dist) / denom;  // the time it hit is the change in location over the place where the velocity vector and normal meet
					var centerVelocityTime:Vertex = Vertex.add(center, Vertex.multiply(dt, velocity));  // s.c + t * v
					var centerToPlaneNormal:Vertex = Vertex.multiply(centerToPlane, line.normal);
					
					collisionPoint = Vertex.subtract(centerToPlaneNormal, centerVelocityTime);
					return true;
				}
			}
		}
		
		// collision response on the ball using a rotation matrix
		public function affectBall(line:Line, go:GameObject){
			var speedBonus = 0;
			
			if(go is Spinner){
				var tempSpinner = go as Spinner;
				// linear momentum response
				tempSpinner.vx = Math.abs(((tempSpinner.mass - mass) * vx + 2 * (tempSpinner.mass * vx)) / (mass + tempSpinner.mass));  // gets the velocity of the ball on the bar
				tempSpinner.vy = Math.abs(((tempSpinner.mass - mass) * vy + 2 * (tempSpinner.mass * vy)) / (mass + tempSpinner.mass));  // uses linear momentum

				var velocityVertex = new Vertex(tempSpinner.vx, tempSpinner.vy);   // make a "vector" out of total velocity
				
				var barMomentum:Number = Vertex.getMagnitude(velocityVertex) /** tempSpinner.mass;  // gets linear momentum*/

				var leverArmDisplacement:Number = Vertex.displace(new Vertex(x, y), new Vertex(tempSpinner.x, tempSpinner.y));  // displaces from the ball to the bar topLeftVertex (the pivot point)

				var rotationMomentum:Number = leverArmDisplacement * barMomentum;  // will always hit on end of bar

				var omega = rotationMomentum / tempSpinner.getInertia(leverArmDisplacement);    // solve for omega = L / I
				
				tempSpinner.omega += omega;
			}
			
			if(go is Flipper){
				speedBonus = -.5;
			}
			var angle:Number = line.getAngle();
	
			var cos:Number = Math.cos(angle);
			var sin:Number = Math.sin(angle);
			
			var x1:Number = x - line.a.x;
			var y1:Number = y - line.a.y;
			
			var y2:Number = cos * y1 - sin * x1;
			
			var vy1:Number = cos * vy - sin * vx;
	
			var x2:Number = cos * x1 + sin * y1;
			
			var vx1:Number = cos * vx + sin * vy;
			
			y2 = -shape.radius;
			
			vy1 *= BOUNCE + speedBonus;
			x1 = cos * x2 - sin * y2;
			y1 = cos * y2 + sin * x2;
			vx = cos * vx1 - sin * vy1;
			vy = cos * vy1 + sin * vx1;
			x = line.a.x + x1;
			
			y = line.a.y + y1;
			
			
		}	
	}	
}
